<?php

namespace app\traits;

use app\model\UserToken;
use app\service\HomeService;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;
use Firebase\JWT\SignatureInvalidException;
use support\Redis;
use UnexpectedValueException;

trait JwtAuthModelTrait
{
    public static string $jwt_key = "Cdc#YPTGT!";

    public function getJwtKey(): string
    {
        return static::$jwt_key;
    }

    /**
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function getToken(array $params = []): array
    {
        $id            = $this->{$this->getPk()};
        $host          = request()->host();
        $time          = time();
        $type          = HomeService::instance()->get_second_host();
        $datetime      = new \DateTime(myDate());
        $nexttime      = $datetime->modify('+15 day');
        $params        += [
            'iss'     => $host,
            'aud'     => $host,
            'iat'     => $time,
            'nbf'     => $time,
            'type'    => $type,
            'os_type' => request()->os_type,
            'exp'     => strtotime($nexttime->format('Y-m-d H:i:s')), // 15天
        ];
        $params['jti'] = compact('id');
        $token         = JWT::encode($params, $this->getJwtKey(), 'HS256');
        return compact('token', 'params', 'type');
    }

    /**
     * @param string $jwt
     * @param string $field
     * @return array
     *
     * @throws UnexpectedValueException     Provided JWT was invalid
     * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
     * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
     * @throws BeforeValidException         Provided JWT is trying to be used before it's been created as defined by 'iat'
     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
     *
     */
    public static function parseToken(string $jwt, string $field = 'id,mobile,status,avatar'): array
    {
        $tokenInfo = Redis::get(APP_TOKEN_LOGIN . $jwt);
        if ($tokenInfo) return json_decode($tokenInfo, true);

        JWT::$leeway = 60;
        try {
            $data    = JWT::decode($jwt, static::$jwt_key, ['HS256']);
            $os_type = $data->os_type ?? 0;
            if (empty($os_type)) return [[], 0];
            if (isset(request()->os_type)) if ($os_type != (request()->os_type)) return [[], 0];
            $user_token_os_type = UserToken::getField(['uid' => $data->jti->id], 'os_type', 3600);
            if ($user_token_os_type != $os_type) return [[], 0];
            $model    = new self();
            $where[]  = [$model->getPk(), '=', $data->jti->id];
            $userInfo = $model::getOne($where, $field, 3600);
            $data     = [$userInfo, $data->jti->id];
            Redis::set(APP_TOKEN_LOGIN . $jwt, json_encode($data), 'EX', 60 * 60 * 24 * 1, 1);
            return $data;
        } catch (SignatureInvalidException|ExpiredException|UnexpectedValueException $exception) {
            return [[], 0];
        }
    }

    /**
     * @param string $jwt
     * @param string $field
     * @return array
     *
     * @throws UnexpectedValueException     Provided JWT was invalid
     * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
     * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
     * @throws BeforeValidException         Provided JWT is trying to be used before it's been created as defined by 'iat'
     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
     *
     */
    public static function adminParseToken(string $jwt, string $field = 'id,head_img,username,phone,remark,login_num,status'): array
    {
        $tokenInfo = Redis::get(ADMIN_TOKEN_LOGIN . $jwt);
        if ($tokenInfo) return json_decode($tokenInfo, true);
        return [[], 0];

        JWT::$leeway = 60;
        try {
            $data    = JWT::decode($jwt, static::$jwt_key, ['HS256']);
            $os_type = $data->os_type ?? 0;
            if (empty($os_type)) return [[], 0];
            if (isset(request()->os_type)) if ($os_type != (request()->os_type)) return [[], 0];
            $model    = new self();
            $where[]  = [$model->getPk(), '=', $data->jti->id];
            $userInfo = $model::getOne($where, $field, 3600);
            $data     = [$userInfo, $data->jti->id];
            Redis::set(ADMIN_TOKEN_LOGIN . $jwt, json_encode($data), 'EX', 60 * 60 * 24 * 1, 1);
            return $data;
        } catch (SignatureInvalidException|ExpiredException|UnexpectedValueException $exception) {
            return [[], 0];
        }
    }
}